package com.pig4cloud.pig.module.util;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.pig4cloud.pig.module.api.entity.Parameter;
import com.pig4cloud.pig.module.api.entity.electric.ElectricMeter;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;

/**
 * @创建者：zhouwei
 * @创建时间：2022/8/31
 * @描述：
 */
public class ElectricMeterUtil {

    /**
     *  抄表
     *
     *  3:正向有功总电能
     *  4:反向有功总电能
     *  5:正向无功总电能
     *  6:反向无功总电能
     *  22:剩余金额
     *  27:A相电流
     *  30:A相电压
     *  33:瞬时有功功率
     *  34:瞬时无功功率
     *  35:读取费率电价
     *  38:读取一级报警金额
     *  39:读取二级报警金额
     *  40:读取电流互感器变比
     *  106:查询继电器状态
     * @return
     */
    public static String getSearchData(Parameter parameter, ElectricMeter meter, String notify_url, int type){
        String url = "http://api2.tqdianbiao.com/Api_v2/ele_read";
        // 请求内容，调用接口所需要的数据（抄电表数据）
        List<Map<String, Object>> req = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();
        item.put("opr_id", generateOperateId());
        item.put("time_out", 0);
        item.put("must_online", true);
        item.put("retry_times", 1);
        item.put("cid", meter.getConcentratorSn());//集中器编号
        item.put("address", meter.getDeviceSn());//电表地址
        item.put("type", type);
        req.add(item);

        String request_content = JSONUtil.toJsonStr(req);
        requestApiAsync(url, request_content,parameter,notify_url);
        return "";
    }

    /**
     *  电表合闸
     */
    private static void openElectric(Parameter parameter, ElectricMeter meter,String notify_url) {
        String url = "http://api2.tqdianbiao.com/Api_v2/ele_control";

        // 请求内容，调用接口所需要的数据（电表合闸）
        List<Map<String, Object>> req = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();

        item.put("opr_id", generateOperateId());
        item.put("time_out", 0);
        item.put("must_online", true);
        item.put("retry_times", 1);
        item.put("cid", meter.getConcentratorSn());//集中器编号
        item.put("address", meter.getDeviceSn());//电表地址
        item.put("type", 11);
        req.add(item);

        String request_content = JSONUtil.toJsonStr(req);
        requestApiAsync(url, request_content,parameter,notify_url);
    }

    /**
     *  电表拉闸
     */
    private static void closeElectric(Parameter parameter,ElectricMeter meter, String notify_url) {
        String url = "http://api2.tqdianbiao.com/Api_v2/ele_control";

        // 请求内容，调用接口所需要的数据（电表拉闸）
        List<Map<String, Object>> req = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();

        item.put("opr_id", generateOperateId());
        item.put("time_out", 0);
        item.put("must_online", true);
        item.put("retry_times", 1);
        item.put("cid", meter.getConcentratorSn());//集中器编号
        item.put("address", meter.getDeviceSn());//电表地址
        item.put("type", 10);
        req.add(item);

        String request_content = JSONUtil.toJsonStr(req);
        requestApiAsync(url, request_content,parameter,notify_url);
    }

    /**
     * 电表清零_同步模式
     */
    private static void clearElectric(Parameter parameter,ElectricMeter meter, String notify_url) {
        String url = "http://api2.tqdianbiao.com/Api_v2/ele_security/reset";

        // 请求内容，调用接口所需要的数据（电表清零，同步模式）
        List<Map<String, Object>> req = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();

        item.put("opr_id", generateOperateId());
        item.put("time_out", 0);
        item.put("must_online", true);
        item.put("retry_times", 1);
        item.put("cid", meter.getConcentratorSn());//集中器编号
        item.put("address", meter.getDeviceSn());//电表地址

        req.add(item);

        String request_content = JSONUtil.toJsonStr(req);
        requestApiAsync(url, request_content,parameter,notify_url);

    }

    /**
     * 电表清零_非同步模式
     */
    private static void clearElectricAsync(Parameter parameter, ElectricMeter meter,String notify_url) {
        String url = "http://api2.tqdianbiao.com/Api_v2/ele_security/reset";

        // 请求内容，调用接口所需要的数据（电表清零，非同步模式）
        List<Map<String, Object>> req = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();
        Map<String, Object> params = new HashMap<>();
        params.put("account_id", "123456");

        item.put("opr_id", generateOperateId());
        item.put("time_out", 0);
        item.put("must_online", true);
        item.put("retry_times", 1);
        item.put("cid", meter.getConcentratorSn());//集中器编号
        item.put("address", meter.getDeviceSn());//电表地址
        item.put("params", params);

        req.add(item);

        String request_content = JSONUtil.toJsonStr(req);
        requestApiAsync(url, request_content,parameter,notify_url);

    }


    /**
     * 电表充值_同步模式
     */
    private static void rechargeElectric(Parameter parameter, ElectricMeter meter,String notify_url) {
        String url = "http://api2.tqdianbiao.com/Api_v2/ele_security/recharge";

        // 请求内容，调用接口所需要的数据（电表充值，同步模式）
        List<Map<String, Object>> req = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();
        Map<String, Object> params = new HashMap<>();
        params.put("money", "100");

        item.put("opr_id", generateOperateId());
        item.put("time_out", 0);
        item.put("must_online", true);
        item.put("retry_times", 1);
        item.put("cid", meter.getConcentratorSn());//集中器编号
        item.put("address", meter.getDeviceSn());//电表地址
        item.put("params", params);

        req.add(item);

        String request_content = JSONUtil.toJsonStr(req);
        requestApiAsync(url, request_content,parameter,notify_url);
    }

    /**
     * 电表充值_非同步模式
     */
    private static void rechargeElectricAsync(Parameter parameter,ElectricMeter meter, String notify_url) {
        String url = "http://api2.tqdianbiao.com/Api_v2/ele_security/recharge";

        // 请求内容，调用接口所需要的数据（电表充值，非同步模式）
        List<Map<String, Object>> req = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();
        Map<String, Object> params = new HashMap<>();
        params.put("money", "100");
        params.put("account_id", "123456");
        params.put("count", "2");

        item.put("opr_id", generateOperateId());
        item.put("time_out", 0);
        item.put("must_online", true);
        item.put("retry_times", 1);
        item.put("cid", meter.getConcentratorSn());//集中器编号
        item.put("address", meter.getDeviceSn());//电表地址
        item.put("params", params);

        req.add(item);

        String request_content = JSONUtil.toJsonStr(req);
        requestApiAsync(url, request_content,parameter,notify_url);
    }

    /**
     * 电表开户_同步模式
     */
    private static void openAccount(Parameter parameter,ElectricMeter meter, String notify_url) {
        String url = "http://api2.tqdianbiao.com/Api_v2/ele_security/open_acount";

        // 请求内容，调用接口所需要的数据（电表开户，同步模式）
        List<Map<String, Object>> req = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();
        Map<String, Object> params = new HashMap<>();
        params.put("money", "100");

        item.put("opr_id", generateOperateId());
        item.put("time_out", 0);
        item.put("must_online", true);
        item.put("retry_times", 1);
        item.put("cid", meter.getConcentratorSn());//集中器编号
        item.put("address", meter.getDeviceSn());//电表地址
        item.put("params", params);

        req.add(item);

        String request_content = JSONUtil.toJsonStr(req);
        requestApiAsync(url, request_content,parameter,notify_url);
    }

    /**
     * 电表开户_非同步模式
     */
    private static void openAccountAsync(Parameter parameter,ElectricMeter meter, String notify_url) {
        String url = "http://api2.tqdianbiao.com/Api_v2/ele_security/open_acount";

        // 请求内容，调用接口所需要的数据（电表开户，非同步模式）
        List<Map<String, Object>> req = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();
        Map<String, Object> params = new HashMap<>();
        params.put("money", "100");
        params.put("account_id", "123456");
        params.put("count", "1");

        item.put("opr_id", generateOperateId());
        item.put("time_out", 0);
        item.put("must_online", true);
        item.put("retry_times", 1);
        item.put("cid", "19020618114");
        item.put("address", "000066660942");
        item.put("params", params);

        req.add(item);

        String request_content = JSONUtil.toJsonStr(req);
        requestApiAsync(url, request_content,parameter,notify_url);
    }


    /**
     *  设置电表参数
     *  type 操作类型
     *  12:设置费率电价
     *  23:设置电流互感器变比
     *  24:设置一级报警金额
     *  25:设置二级报警金额
     */
    private static void setElectric(Parameter parameter,ElectricMeter meter, String notify_url) {
        String url = "http://api2.tqdianbiao.com/Api_v2/ele_write";

        // 请求内容，调用接口所需要的数据（一级报警值）
        List<Map<String, Object>> req = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();
        Map<String, Object> params = new HashMap<>();
        params.put("p1", "300");

        item.put("opr_id", generateOperateId());
        item.put("time_out", 0);
        item.put("must_online", true);
        item.put("retry_times", 1);
        item.put("cid", meter.getConcentratorSn());//集中器编号
        item.put("address", meter.getDeviceSn());//电表地址
        item.put("type", 24);
        item.put("params", params);
        req.add(item);

        String request_content = JSONUtil.toJsonStr(req);
        requestApiAsync(url, request_content, parameter,notify_url);
    }

    public static String generateOperateId() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static String requestApiAsync(String url, String request_content,Parameter parameter, String notify_url) {
        String name = Thread.currentThread().getStackTrace()[2].getMethodName();
        System.out.println(name);

        String response = requestAsync(url, request_content,parameter, notify_url);
//        printResponse(response);
        return response;
    }

    // 打印响应内容
    public static void printResponse(String response) {
        JSONObject jsonObject = JSONUtil.parseObj(response);
        System.out.println("请求参数： " + jsonObject.toString());

        String status = jsonObject.getStr("status");
        if(!"SUCCESS".equals(status)) {
            System.out.println(jsonObject.getStr("error_msg"));
        } else {
            String response_content = jsonObject.getStr("response_content");
            System.out.println("response_content: " + response_content);
            JSONArray contentArray = JSONUtil.parseArray(response_content);
            int index = 1;
            System.out.println("返回结果：");
            for(int i = 0; i < contentArray.size(); ++i) {
                System.out.println("[" + index++ + "]");
                JSONObject contentObject = contentArray.getJSONObject(i);
                Set<String> keySet = contentObject.keySet();
                for(String key: keySet) {
                    System.out.println(key + ": " + contentObject.get(key));
                }
            }
        }
    }

    // 请求接口
    private static String request(String url, String request_content,String auth_code, String nonce){
        // 时间戳
        String timestamp = String.valueOf(new Date().getTime()/1000);

        // 用于签名的内容
        Map<String, String> data = new HashMap<>();
        data.put("timestamp", timestamp);
        data.put("auth_code", auth_code);
        data.put("request_content", request_content);

        // 获取签名
        String sign = getSign(data,nonce);

        data.put("sign", sign);

        try {
            return sendHttpRequest(url, data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    // 请求接口
    private static String requestAsync(String url, String request_content, Parameter parameter, String notify_url){
        // 时间戳
        String timestamp = String.valueOf(new Date().getTime() / 1000);

        // 用于签名的内容
        Map<String, String> data = new HashMap<>();
        data.put("timestamp", timestamp);
        data.put("auth_code", parameter.getElectricMeterAuthCode());
        data.put("request_content", request_content);
        data.put("notify_url", notify_url);

        // 获取签名
        String sign = getSign(data,parameter.getElectricMeterNonceStr());

        data.put("sign", sign);

        try {
            return sendHttpRequest(url, data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    // 生成签名字符串
    private static String getSign(Map<String, String> data,String nonce) {
        // 获取关键字列表
        List<String> keys = new ArrayList<>(data.keySet());
        // 关键字列表排序
        keys.sort(Comparator.naturalOrder());
        StringBuilder sb = new StringBuilder();
        for (String key : keys)
        {
            // 取各个字段内容拼接字符串
            sb.append(data.get(key));
        }
        // 加上双方约定随机字符串
        String txt = sb.toString() + nonce;

        // 计算哈希值
        return getMD5(txt);
    }


    // md5加密
    private static String getMD5(String password) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        byte[] byteArray = password.getBytes(StandardCharsets.UTF_8);

        byte[] md5Bytes = md5.digest(byteArray);
        StringBuilder hexValue = new StringBuilder();
        for (byte md5Byte : md5Bytes) {
            int val = ((int) md5Byte) & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }


    public static boolean checkSign(String response_content, String timestamp, String sign,String nonce) {
        // 随机字符串 后台获取
        String buf = response_content + timestamp + nonce;
        String encode = getMD5(buf);
        return encode.equals(sign);
    }


    // 发送http请求
    private static String sendHttpRequest(String url, Map<String, String> bodyMap) throws Exception {
        System.out.println("请求地址：" + url);
        System.out.println("发送参数：" + bodyMap.toString());
        HttpClient client = HttpClientBuilder.create().build();
        HttpPost postRequest = new HttpPost(url);

        List<NameValuePair> nvps = new ArrayList<>();

        for(String key : bodyMap.keySet()) {
            nvps.add(new BasicNameValuePair(key,bodyMap.get(key)));
        }
        postRequest.setEntity(new UrlEncodedFormEntity(nvps,"utf-8"));


        int retry = 3;
        HttpResponse execute = null;
        while(retry-- > 0) {
            try {
                execute = client.execute(postRequest);
                break;
            } catch (Exception e) {
                Thread.sleep(5000);
            }
        }
        if(execute == null) {
            throw new Exception("接口请求失败");
        }
        String resp = EntityUtils.toString(execute.getEntity(), "UTF-8");
        System.out.println("接口返回：" + resp);
        return resp;
    }
}
